////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.transport {

	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.StatusEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;

	import ru.goodcore.logging.ILogger;
	import ru.goodcore.logging.Log;
	import ru.goodcore.logging.LogEventLevel;
	import ru.goodcore.net.URI;
	import ru.goodcore.utils.ObjectUtil;

	/**
	 *  @author				Dimarik
	 *  @version			1.0
	 *  @langversion		3.0
	 *  @playerversion		9.0
	 *
	 */
	public class HTTPTransport extends AbstractTransport {

		//---------------------------------------------------------------------
		//
		//   Private class's variables
		//
		//---------------------------------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Class's methods
		//
		//---------------------------------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Constructor
		//
		//---------------------------------------------------------------------

		public function HTTPTransport() {
			super();

			this.loader.dataFormat = URLLoaderDataFormat.BINARY;
		}

		//---------------------------------------------------------------------
		//
		//   Overriden properties: AbstractTransport
		//
		//---------------------------------------------------------------------

		public override function get logger():ILogger {
			if ( !super.$logger ) {
				this.$logger = Log.getLogger('HTTPTransport');
			}
			return this.$logger;
		}

		//---------------------------------------------------------------------
		//
		//   Properties
		//
		//---------------------------------------------------------------------

		//-----------------------------
		//   url
		//-----------------------------
		private var _url:	String;

		public function get url():String {
			return this._url;
		}

		public function set url(value:String):void {
			if (this._url == value) return;
			this._url = value;
		}

		//-----------------------------
		//   method
		//-----------------------------
		private var _method:	String = URLRequestMethod.POST;

		public function get method():String {
			return this._method;
		}

		public function set method(value:String):void {
			if (this._method == value) return;
			this._method = value;
		}

		//-----------------------------
		//   dataFormat
		//-----------------------------
		private var _dataFormat:	String;

		public function get dataFormat():String {
			return this.loader.dataFormat;
		}

		public function set dataFormat(value:String):void {
			if (this.loader.dataFormat == value) return;
			this.loader.dataFormat = value;
		}

		//-----------------------------
		//   httpStatus
		//-----------------------------
		private var _httpStatus:	int;

		public function get httpStatus():int {
			return this._httpStatus;
		}

		//---------------------------------------------------------------------
		//
		//   Protected constants
		//
		//---------------------------------------------------------------------

		protected var loader:			URLLoader = new URLLoader();

		protected var currentRequest:	Object;

		//---------------------------------------------------------------------
		//
		//   Private constants
		//
		//---------------------------------------------------------------------

		private const _queue:			Array = new Array();

		//---------------------------------------------------------------------
		//
		//   Private variables
		//
		//---------------------------------------------------------------------

		/**
		 * @private 
		 */
		private var _urlRequest:		URLRequest;

		//---------------------------------------------------------------------
		//
		//   Public methods
		//
		//---------------------------------------------------------------------

		public override function connect(uri:URI):void {
			this.clear();

			// Just check is destination available
			var url:String = uri.toDisplayString();
			this._urlRequest = new URLRequest(url);
			this.loader.load(this._urlRequest);

			this.loader.addEventListener(Event.COMPLETE, 					this.handler_successConnect);
			this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
			this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
			this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);
		}


		public override function send(options:Object = null):void {
			var requestObject:Object = new Object();
			requestObject.options = options;

			this._queue.push(requestObject);

			if( !this.currentRequest ) this.requestNext();
		}


		public override function close():void {
			super.connected = false;

			try {
				this.loader.close();
			} catch (error:Error) {
				// ignore
			}

		}

		//---------------------------------------------------------------------
		//
		//   Protected methods
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		protected function requestNext():void {
			if (this._queue.length == 0) return;

			this.currentRequest = this._queue.shift();

			var options:Object = this.currentRequest.options;
			
			
			if (options) {
				this._urlRequest.data = options;
				this._urlRequest.method = URLRequestMethod.POST
			} else {
				this._urlRequest.data = null;
				this._urlRequest.method = URLRequestMethod.GET;
			}

			var arr:Array = new Array(LogEventLevel.INFO, 'OUT: ' + ObjectUtil.toString(options));
			this.logger.log.apply(null, arr);

			this.loader.load(this._urlRequest);
		}

		//---------------------------------------------------------------------
		//
		//   Private methods
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		private function clear():void {
			// Clear queue
			this._queue.splice(0, this._queue.length);
			this.currentRequest = null;
		}

		//---------------------------------------------------------------------
		//
		//   Event handlers
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 * Connect successfull!
		 */
		protected function handler_successConnect(event:Event):void {
			this.loader.removeEventListener(Event.COMPLETE, 					this.handler_successConnect);
			this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
			this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
			this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);

			this.loader.addEventListener(Event.COMPLETE, 					this.handler_success);
			this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
			this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
			this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);

			super.connected = true;

			super.dispatchEvent( new Event(Event.CONNECT) );
		}

		/**
		 * @private
		 */
		protected function handler_faultConnect(event:Event):void {
			this.loader.removeEventListener(Event.COMPLETE, 					this.handler_successConnect);
			this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
			this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
			this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);

			super.dispatchEvent( event );
		}

		/**
		 * @private
		 */
		protected function handler_httpStatusConnect(event:HTTPStatusEvent):void {
			super.dispatchEvent( new Event(StatusEvent.STATUS) );
		}

		/**
		 * @private
		 */
		protected function handler_success(event:Event):void {
			super.data = (event.target as URLLoader).data;
			super.dispatchEvent( new ProgressEvent(ProgressEvent.SOCKET_DATA) );
			this.currentRequest = null;
			this.requestNext();
		}

		/**
		 * @private
		 */
		protected function handler_fault(event:Event):void {
			this.loader.removeEventListener(Event.COMPLETE, 					this.handler_success);
			this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
			this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
			this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);

			super.connected = false;

			super.dispatchEvent(new Event(Event.CLOSE) );
		}

		/**
		 * @private
		 */
		protected function handler_httpStatus(event:HTTPStatusEvent):void {
			super.dispatchEvent( new Event(StatusEvent.STATUS) );
			this._httpStatus = event.status;
		}

	}
}